package org.eclipseuseradmin.ui.actions;

import java.util.Map;

import org.eclipse.core.commands.Command;
import org.eclipse.core.commands.CommandEvent;
import org.eclipse.core.commands.ICommandListener;
import org.eclipse.core.commands.ParameterizedCommand;
import org.eclipse.core.commands.common.NotDefinedException;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IAction;
import org.eclipse.swt.widgets.Event;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.commands.ICommandImageService;
import org.eclipse.ui.commands.ICommandService;
import org.eclipse.ui.handlers.IHandlerService;
import org.eclipse.ui.services.IServiceLocator;

public abstract class UserAdminUiActionFactory {

	/**
	 * Id of actions created by this action factory.
	 */
	private final String actionId;

	/**
	 * Creates a new workbench action factory with the given id.
	 * 
	 * @param actionId
	 *            the id of actions created by this action factory
	 */
	protected UserAdminUiActionFactory(String actionId) {
		this.actionId = actionId;
	}

	/**
	 * Returns the id of this action factory.
	 * 
	 * @return the id of actions created by this action factory
	 */
	public String getId() {
		return actionId;
	}

	public abstract IAction create(IWorkbenchWindow window);

	public static final UserAdminUiActionFactory LOGIN = new UserAdminUiActionFactory(
			"login") {

		public IAction create(IWorkbenchWindow window) {
			if (window == null) {
				throw new IllegalArgumentException();
			}

			CommandAction action = new CommandAction(window,
					"org.eclipseuseradmin.ui.commands.login");
			action.setId(getId());
			action.setText("login");
			action.setToolTipText("tool");

			return action;
		}
	};

	public static final UserAdminUiActionFactory LOGOUT = new UserAdminUiActionFactory(
			"logout") {

		public IAction create(IWorkbenchWindow window) {
			if (window == null) {
				throw new IllegalArgumentException();
			}

			CommandAction action = new CommandAction(window,
					"org.eclipseuseradmin.ui.commands.logout");
			action.setId(getId());
			action.setText("logout");
			action.setToolTipText("tool");

			return action;
		}
	};

	private class CommandAction extends Action {

		private IHandlerService handlerService;

		private ParameterizedCommand command;

		private ICommandListener commandListener;

		/**
		 * Creates the action backed by a command. For commands that don't take
		 * parameters.
		 * 
		 * @param serviceLocator
		 *            The service locator that is closest in lifecycle to this
		 *            action.
		 * @param commandIdIn
		 *            the command id. Must not be <code>null</code>.
		 */
		public CommandAction(IServiceLocator serviceLocator, String commandIdIn) {
			this(serviceLocator, commandIdIn, null);
		}

		/**
		 * Creates the action backed by a parameterized command. The
		 * parameterMap must contain only all required parameters, and may
		 * contain the optional parameters.
		 * 
		 * @param serviceLocator
		 *            The service locator that is closest in lifecycle to this
		 *            action.
		 * @param commandIdIn
		 *            the command id. Must not be <code>null</code>.
		 * @param parameterMap
		 *            the parameter map. May be <code>null</code>.
		 */
		public CommandAction(IServiceLocator serviceLocator,
				String commandIdIn, Map parameterMap) {
			if (commandIdIn == null) {
				throw new IllegalArgumentException();
			}
			init(serviceLocator, commandIdIn, parameterMap);
		}

		protected ICommandListener getCommandListener() {
			if (commandListener == null) {
				commandListener = new ICommandListener() {
					public void commandChanged(CommandEvent commandEvent) {
						if (commandEvent.isHandledChanged()
								|| commandEvent.isEnabledChanged()) {
							if (commandEvent.getCommand().isDefined()) {
								setEnabled(commandEvent.getCommand()
										.isEnabled());
							}
						}
					}
				};
			}
			return commandListener;
		}

		/**
		 * Build a command from the executable extension information.
		 * 
		 * @param commandService
		 *            to get the Command object
		 * @param commandId
		 *            the command id for this action
		 * @param parameterMap
		 */
		private ParameterizedCommand getCommand(ICommandService commandService,
				String commandId, Map parameterMap) {

			Command cmd = commandService.getCommand(commandId);
			if (!cmd.isDefined()) {
				// log
				return null;
			}

			if (parameterMap == null) {
				return  new ParameterizedCommand(cmd, null);
			}

			return ParameterizedCommand.generateCommand(cmd,
					parameterMap);
		}

		public void dispose() {
			// not important for command ID, maybe for command though.
			handlerService = null;
			if (commandListener != null) {
				command.getCommand().removeCommandListener(commandListener);
				commandListener = null;
			}
			command = null;
		}

		/*
		 * (non-Javadoc)
		 * 
		 * @see
		 * org.eclipse.jface.action.Action#runWithEvent(org.eclipse.swt.widgets
		 * .Event)
		 */
		public void runWithEvent(Event event) {
			if (handlerService == null || command == null) {
				return;
			}
			try {
				handlerService.executeCommand(command, event);
			} catch (Exception e) {
				// log
			}
		}

		/*
		 * (non-Javadoc)
		 * 
		 * @see org.eclipse.jface.action.Action#run()
		 */
		public void run() {
			// hopefully this is never called
			runWithEvent(null);
		}

		protected void init(IServiceLocator serviceLocator, String commandIdIn,
				Map parameterMap) {

			handlerService = (IHandlerService) serviceLocator
					.getService(IHandlerService.class);
			ICommandService commandService = (ICommandService) serviceLocator
					.getService(ICommandService.class);
			ICommandImageService commandImageService = (ICommandImageService) serviceLocator
					.getService(ICommandImageService.class);

			command = getCommand(commandService, commandIdIn, parameterMap);
			if (command != null) {
				setId(command.getId());
				try {
					setText(command.getName());
				} catch (NotDefinedException e) {
					// if we get this far it shouldn't be a problem
				}
				command.getCommand().addCommandListener(getCommandListener());
				command.getCommand().setEnabled(
						handlerService.getCurrentState());
				setEnabled(command.getCommand().isEnabled());
				setImageDescriptor(commandImageService.getImageDescriptor(
						commandIdIn, ICommandImageService.TYPE_DEFAULT));
				setDisabledImageDescriptor(commandImageService
						.getImageDescriptor(commandIdIn,
								ICommandImageService.TYPE_DISABLED));
				setHoverImageDescriptor(commandImageService.getImageDescriptor(
						commandIdIn, ICommandImageService.TYPE_HOVER));
			}
		}

		protected ParameterizedCommand getCommand() {
			return command;
		}

		public String getActionDefinitionId() {
			if (command == null) {
				return null;
			}
			return command.getId();

		}
	}

}
